SPB Git forge
15commits 1branches 0releases
29.7 MBsize
maindefault branch
10 days agolast push
TypeScript 36.3% Python 31.8% Go 18% JavaScript 9.8% Shell 1.9% SQL 1.4% CSS 0.5%
4.7 KB · 107 lines tsx
Raw Blame History
1import type { Metadata } from 'next';2import Link from 'next/link';3import { ComponentGrid } from '@/components/detail/ComponentGrid';4import { ScopeHeader } from '@/components/detail/ScopeHeader';5import { ScopePressureChart } from '@/components/detail/ScopeCharts';6import { LatencyMatrixTable, LinkList, ProbesTable, TargetsTable } from '@/components/detail/Tables';7import { IncidentsSection } from '@/components/incidents/IncidentsSection';8import { Section, Stat } from '@/components/ui/primitives';9import { apiGet, apiTry } from '@/lib/api';10import { fmt, fmtDelta, fmtInt, fmtPct } from '@/lib/format';11import type { CountryDetailResponse, Latency } from '@/lib/types';1213export const dynamic = 'force-dynamic';1415type Params = Promise<{ cc: string }>;1617export async function generateMetadata({ params }: { params: Params }): Promise<Metadata> {18  const { cc } = await params;19  const c = await apiTry<CountryDetailResponse>(`/api/v1/pressure/country/${encodeURIComponent(cc.toUpperCase())}`);20  if (!c) return { title: 'Country' };21  return { title: `${c.name} — Internet pressure ${fmt(c.pressure)} (${c.level_label})`, description: `Live Internet pressure observed for ${c.name}: ${fmt(c.pressure)} ${c.level_label}, ${fmtDelta(c.delta_1h)} over 1 h, from ${c.probes.length} probes and ${c.targets.length} anchored targets.`, alternates: { canonical: `/country/${cc.toLowerCase()}` } };22}2324export default async function CountryPage({ params }: { params: Params }) {25  const { cc } = await params;26  const [c, latency] = await Promise.all([apiGet<CountryDetailResponse>(`/api/v1/pressure/country/${encodeURIComponent(cc.toUpperCase())}`), apiTry<Latency>('/api/v1/latency')]);27  const first = c.history_24h.points[0]?.pressure;28  const change24 = first != null ? c.pressure - first : null;29  const matrix = (latency?.matrix ?? []).filter((m) => m.from === c.region || m.to === c.region);30  return (31    <div className="pb-8">32      <ScopeHeader33        kicker={34          <>35            Country · region{' '}36            <Link href={`/internet/${c.region}`} className="text-accent hover:underline">37              {c.region}38            </Link>39          </>40        }41        title={c.name}42        subtitle={43          <>44            {c.probes.length} probe{c.probes.length === 1 ? '' : 's'} · {fmtInt(c.targets.length)} anchored targets · role {c.role}45            {!c.coverage_ok && <span className="text-warn"> · weak coverage</span>}46          </>47        }48        pressure={c.pressure}49        level={c.level}50        delta1h={c.delta_1h}51        trend={c.trend}52        meta={53          <>54            <span>55              Δ24h <span className="text-ink">{fmtDelta(change24)}</span>56            </span>57            <span>58              7d median <span className="text-ink">{fmt(c.baseline_7d.median)}</span> · p90 <span className="text-ink">{fmt(c.baseline_7d.p90)}</span>59            </span>60            <span>61              ISO <span className="text-ink">{c.cc}</span>62            </span>63          </>64        }65      />6667      <Section label="Components">68        <ComponentGrid components={c.components} />69      </Section>7071      <Section label="24 h" right={<span>vs 7-day baseline</span>}>72        <ScopePressureChart series={c.history_24h} baseline={c.baseline_7d} height={240} />73      </Section>7475      <Section label="Latency matrix" right={<span>region {c.region} pairs</span>}>76        <LatencyMatrixTable rows={matrix} highlight={c.region} />77      </Section>7879      <Section label="Incidents">80        <IncidentsSection incidents={c.incidents} />81      </Section>8283      <div className="grid grid-cols-[minmax(0,1fr)] gap-x-10 lg:grid-cols-2">84        <Section label="Affected / observed ASNs" className="min-w-0">85          <LinkList items={c.asns.map((a) => ({ key: String(a.asn), name: a.name, pressure: a.pressure, sub: `AS${a.asn}` }))} hrefFor={(k) => `/asn/${k}`} label="ASNs" />86        </Section>87        <Section label="Services" className="min-w-0">88          <LinkList items={c.services.map((s) => ({ key: s.slug, name: s.name, pressure: s.pressure, sub: `avail ${fmtPct(s.observed_availability_24h, 2)}` }))} hrefFor={(k) => `/service/${k}`} label="services" />89        </Section>90      </div>9192      <Section label="Probe coverage">93        <div className="mb-3 grid grid-cols-3 gap-4">94          <Stat label="probes" value={fmtInt(c.probes.length)} />95          <Stat label="targets" value={fmtInt(c.targets.length)} />96          <Stat label="coverage" value={c.coverage_ok ? 'ok' : 'weak'} />97        </div>98        <ProbesTable probes={c.probes} compact />99      </Section>100101      <Section label="Targets anchored here" right={<Link href="/targets" className="hover:text-ink">target registry →</Link>}>102        <TargetsTable targets={c.targets} showCategory />103      </Section>104    </div>105  );106}107